import java.util.Scanner;
import java.util.ArrayList;
import java.util.HashMap;
import ZorkObjects.*;

public final class ZorkGame
{
    private ArrayList<ZorkObject> inventory;
    private ZorkMap gameMap;
    private Scanner inputScanner;
    private HashMap<String, String> directionMap;
    private boolean gameDone = false;
	private ZorkObject toRemove = null;

	private Room currentRoom;

    public ZorkGame(ZorkMap gameMap)
    {
        this.gameMap = gameMap;
        inputScanner = new Scanner(System.in);
        inventory = new ArrayList<ZorkObject>();
        directionMap = new HashMap<String, String>();

        directionMap.put("n", "north");
        directionMap.put("e", "east");
        directionMap.put("s", "south");
        directionMap.put("w", "west");
    }

	private void printInventory()
	{
		System.out.print("Inventory: ");

		if(inventory.size() == 0) {
			System.out.println("empty");
		}
		else
		{
			for(int i = 0; i < inventory.size(); i++)
			{
				ZorkObject z = inventory.get(i);
				System.out.print(z.getName());

				if(i == inventory.size() - 1) {
					System.out.print(" ");
				} 
                else {
					System.out.print(", ");
				}
			}
			System.out.println();
		}
	}

	private void takeItem(String itemName)
	{
		Item toRemove = null;
		boolean breaked = false;

		for(Item i : currentRoom.getItems())
		{
			if(i.getName().equals(itemName))
			{
				inventory.add(i);
				toRemove = i;
				breaked = true;
				break;
			}
		}

		for(Container c : currentRoom.getContainers())
		{
            if(c.isOpened())
            {
                for(Item i : c.getItems())
                {
                    if(i.getName().equals(itemName))
                    {
						String itemStatus = i.getStatus();
						if("not-taken".equals(itemStatus)) {
							i.setStatus("taken");
						}

                        inventory.add(i);
                        toRemove = i;
                        breaked = true;
                        break;
                    }
                }

                if(toRemove != null) {
                    c.removeItem(toRemove);
                }
            }
		}

        if(toRemove != null)
        {
            System.out.println(String.format(
                    "Item %s added to inventory.", itemName));
            currentRoom.getItems().remove(toRemove);
        }

		if(!breaked)
        {
			System.out.println("Error");
            return;
		}
	}

	private boolean meetSingleTriggerCondition(Condition condition)
	{
		if(condition.getTriggered()) {
			return false;
		}

		condition.setTriggered();

		return meetCondition(condition);
	}

	private boolean meetCondition(Condition condition)
	{
        ZorkObject item = null;
        String currentOwner = null;
		String objectName = condition.getObject();
        String requiredOwner = condition.getOwner();
        String requiredStatus = condition.getStatus();

		for(ZorkObject z : inventory)
		{
			if(z.getName().equals(objectName))
            {
                currentOwner = "inventory";
                item = z;
			}
		}

        for(Container c : currentRoom.getContainers())
        {
            if(c.getName().equals(objectName)) {
                item = c;
            }

            for(ZorkObject z : c.getItems())
            {
                if(z.getName().equals(objectName))
                {
                    currentOwner = c.getName();
                    item = z;
                }
            }
        }

        if(item == null)
        {
            for(ZorkObject z : currentRoom.getItems())
            {
                if(z.getName().equals(objectName))
                {
                    item = z;
                }
            }
        }

        if(requiredStatus != null)
        {
            if(!requiredStatus.equals(item.getStatus())) {
                return false;
            }
        }

        //If the required owner is something
        if(requiredOwner != null)
        {
            //If that owner has to have it
            if(condition.getHas())
            {
                if(!requiredOwner.equals(currentOwner)) {
                    return false;
                }
            }
            else {
                if(requiredOwner.equals(currentOwner)) {
                    return false;
                }
            }
        }

        return true;
	}

	private void changeRooms(String roomName)
	{
		for(Room r : gameMap.getRooms())
		{
			if(r.getName().equals(roomName))
			{
				currentRoom = r;
				System.out.println(currentRoom.getDescription());

				if("exit".equals(currentRoom.getType())) {
					gameDone = true;
				}
			}
		}
	}	

    private void openContainer(String command)
    {
        String[] commandParts = command.split(" ");
        ArrayList<Container> roomContainers = currentRoom.getContainers();
        boolean error = true;

        for(Container c : roomContainers)
        {
            ArrayList<Trigger> containerTriggers = c.getTriggers();
            for(Trigger t : containerTriggers) 
            {
                if(meetCondition(t.getCondition())) {
                    System.out.println(t.getPrint());
                    return;
                }
            }

            if(c.getName().equals(commandParts[1]))
            {
                if(c.getItems().size() > 0)
                {
                    ArrayList<Item> containerItems = c.getItems();
                    for(Item i : containerItems) 
                    {
                        System.out.println(
                            String.format("%s contains %s.", 
                            commandParts[1], i.getName()));
                    }
                }
                else {
                    System.out.println(
                        String.format("%s is empty.", c.getName()));
                }

                c.setOpened();
                error = false;
            }
        }

        if(error) {
            System.out.println("Error");
        }
    }

    private void dropItem(String command)
    {
        String[] commandParts = command.split(" ");
        Item toRemove = null;

        for(ZorkObject z : inventory)
        {
            if(z.getName().equals(commandParts[1]))
            {
                toRemove = (Item)z;
            }
        }

        if(toRemove != null)
        {
            inventory.remove(toRemove);
            currentRoom.getItems().add(toRemove);
            System.out.println(String.format("%s dropped.", toRemove.getName()));
        }
        else {
            System.out.println("Error");
        }
    }

    //Returns whether or not the currentRoom has been changed
    private boolean processDirection(String command)
    {
		boolean goodWay = false;
		boolean canChange = true;

        for(Border b : currentRoom.getBorders())
        {
            if(b.getDirection().equals(directionMap.get(command)))
            {
				goodWay = true;
                ArrayList<Trigger> roomTriggers = currentRoom.getTriggers();

				boolean matchingCommand = false;
				for(Trigger t : roomTriggers)
				{
					if(t.getCommand().equals(command)) {
						matchingCommand = true;
					}
				}

				if(matchingCommand)
				{
					for(Trigger t : roomTriggers) 
					{
						if(t.getCommand().equals(command))
						{
							if(meetCondition(t.getCondition()))
							{
								System.out.println(t.getPrint());
								canChange = false;
							}
							else
							{
								if(canChange)
								{
									changeRooms(b.getName());
									return true;
								}
							}
						}
					}
				}	
				else
				{
					changeRooms(b.getName());
					return true;
				}
            }
        }

        if(!goodWay)
		{
            System.out.println("Can't go that way.");
            return false;
        }

        return true;
    }

    private void putItem(String command)
    {
        String[] commandParts = command.split(" ");

        if(commandParts.length != 4) {
            System.out.println("Error");
        }
        else 
        {
            Item toAdd = null;
            Container toAddTo = null;
            String containerName = commandParts[3];
            String itemName = commandParts[1];
            boolean foundItem = false;
            boolean foundContainer = false; 

            for(ZorkObject z : inventory)
            {
                if(z.getName().equals(itemName))
                {
                    foundItem = true;
                    toAdd = (Item)z;
                    break;
                }
            }

            for(Container c : currentRoom.getContainers())
            {
                //Make sure the container is empty
                if(c.getName().equals(containerName) && c.getItems().size() == 0)
                {
                    foundContainer = true;
                    toAddTo = c;
                    break;
                }
            }

            if(foundItem && foundContainer)
            {
                toAddTo.addItem(toAdd);
                inventory.remove(toAdd);
                System.out.println(
                    String.format("Item %s added to %s.",
                    toAdd.getName(), toAddTo.getName()));
                for(Trigger t : toAddTo.getTriggers())
                {
                    if(meetCondition(t.getCondition()))
                    {
                        for(String a : t.getActions()) {
                            processAction(a);
                        }
                        System.out.println(t.getPrint());
                    }
                    else {
                        System.out.println(t.getCondition());
                    }
                }
            }
            else {
                System.out.println("Error");
            }
        }
    }

    private void readItem(String command)
    {
        String[] commandParts = command.split(" ");
        boolean printed = false;
        ArrayList<Item> roomItems = currentRoom.getItems();

        for(ZorkObject z : inventory)
        {
            if(z.getName().equals(commandParts[1]))
            {
                System.out.println(((Item)z).getWriting());
                printed = true;
            }
        }

        for(Item i : roomItems)
        {
            if(i.getName().equals(commandParts[1]))
            {
                System.out.println(i.getWriting());
                printed = true;
            }
        }

        if(!printed) {
            System.out.println("Error");
        }
    }

    private void processInput(String command) 
    {
        command = command.trim();
        String[] commandParts = command.split(" ");

		if(command.equals("")) {
			return;
		}

        //Process a directional command
        if(directionMap.keySet().contains(command))
        {
            boolean haveMoved = processDirection(command);
            if(!haveMoved) {
                return;
            }
        }

        //List inventory
        else if(command.toLowerCase().equals("i"))
        {
			printInventory();
        }

        //Take an item
        else if(commandParts[0].equals("take"))
        {
            String itemName = commandParts[1];
			takeItem(itemName);
        }

        //Open a container
        else if(commandParts[0].equals("open"))
        {
            openContainer(command);
        }

        //Read an item
        else if(commandParts[0].equals("read"))
        {
            readItem(command);
        }

        //Drop an item
		else if(commandParts[0].equals("drop"))
		{
            dropItem(command);
		}

        //Put an item into a container
		else if(commandParts[0].equals("put"))
		{
            putItem(command);
		}
		else if(commandParts.length > 2)
		{
			 if(commandParts[0].equals("turn") &&
					commandParts[1].equals("on"))
			{
				String itemName = commandParts[2];

				boolean breaked = false;
				for(ZorkObject z : inventory)
				{
					if(z.getName().equals(itemName))
					{
						turnOnItem((Item)z);
						breaked = true;

						for(Creature c : currentRoom.getCreatures())
						{
							ArrayList<Trigger> creatureTriggers = c.getTriggers();
							for(Trigger t : creatureTriggers)
							{
								if(meetSingleTriggerCondition(t.getCondition())) {
									System.out.println(t.getPrint());
								}
							}
						}
						break;
					}
				}

				if(!breaked) {
					System.out.println("Error");
				}
			}
			else if(commandParts.length > 3)
			{
				if(commandParts[0].equals("attack") &&
						commandParts[2].equals("with"))
				{
					String creatureName = commandParts[1];
					String attackMethod = commandParts[3];
					boolean foundMatch = false;
					boolean hasItem = false;

					for(ZorkObject z : inventory)
					{
						if(z.getName().equals(attackMethod))
						{
							hasItem = true;
							break;
						}
					}

					if(!hasItem) {
						System.out.println("Error");
					}
					else
					{
						// start here
						ArrayList<Creature> roomCreatures = currentRoom.getCreatures();
						for(Creature c : roomCreatures)
						{
							if(c.getName().equals(creatureName) &&
							   c.getVulnerabilities().contains(attackMethod))
							{
								foundMatch = true;
								Attack creatureAttack = c.getAttack();
								Condition attackCondition = creatureAttack.getCondition();

								if(meetCondition(attackCondition))
								{
									System.out.println(String.format(
										"You assult the %s with the %s.", creatureName, attackMethod));
									System.out.println(creatureAttack.getPrint());
									ArrayList<String> attackActions = creatureAttack.getActions();
									for(String action : attackActions) {
										processAction(action);
									}
								} 
								else {
									System.out.println("Error?");
								}
							}
						}

						if(!foundMatch) {
							System.out.println("Error");
						}
					}
				}
			}
		}
        else 
        {
            System.out.println("Error");
        }
    }

	private void deleteObject(ZorkObject toDelete)
	{
		currentRoom.getItems().remove(toDelete);
		currentRoom.getCreatures().remove(toDelete);
	}

	private void processAction(String action)
	{
		String[] actionParts = action.split(" ");
		if(actionParts[0].equals("Add"))
		{
			String addItemName = actionParts[1];
			Item toAdd = null;
			boolean breaked = false;

			for(Item i : gameMap.getGlobalItems())
			{
				if(i.getName().equals(addItemName))
				{
					toAdd = i;
					breaked = true;
					break;
				}
			}

			if(breaked)
			{
				currentRoom.getItems().add(toAdd);
				gameMap.getGlobalItems().remove(toAdd);
			}
		}
		else if(actionParts[0].equals("Delete"))
		{
			String objectToDelete = actionParts[1];

			for(Item i : currentRoom.getItems()) 
			{
				if(i.getName().equals(objectToDelete))
				{
					toRemove = i;
					break;
				}
			}

			for(Creature c : currentRoom.getCreatures())
			{
				if(c.getName().equals(objectToDelete))
				{
					toRemove = c;
					break;
				}
			}
		}
		else if(actionParts[0].equals("Update"))
		{
            for(ZorkObject z : inventory) 
            {
                if(z.getName().equals(actionParts[1])) {
                    z.setStatus(actionParts[3]);
                }
            }

			for(Item i : currentRoom.getItems())
			{
				if(i.getName().equals(actionParts[1])) {
					i.setStatus(actionParts[3]);
				}
			}

			for(Container c : currentRoom.getContainers())
			{
				if(c.getName().equals(actionParts[1])) {
					c.setStatus(actionParts[3]);
				}
			}
		}
	}

	private void turnOnItem(Item item)
	{
		HashMap<String, String> itemTurnOn = item.getTurnOn();
		System.out.println(
			String.format("You activate the %s", item.getName()));
		System.out.println(itemTurnOn.get("print"));
		String action = itemTurnOn.get("action");
		String[] actionParts = action.split(" ");
		item.setStatus(actionParts[3]);
	}

    //"Main" loop
    public void play()
    {
        for(Room r : gameMap.getRooms())
        {
            if(r.getName().equals("Entrance"))
            {
				currentRoom = r;
				break;
            }
        }

		System.out.println(currentRoom.getDescription());

        while(!gameDone)
        {
			deleteObject(toRemove);
			System.out.print(">");
            String input = inputScanner.nextLine();
            processInput(input);
        }
    }
}
